package com.dianping.abel.lock.impl;

import com.dianping.abel.lock.AbstractCacheLock;
import com.dianping.abel.lock.DistributedLock;
import com.dianping.squirrel.client.StoreKey;
import com.dianping.squirrel.client.impl.redis.RedisClientBuilder;
import com.dianping.squirrel.client.impl.redis.RedisStoreClient;

/**
 * 分布式锁:squirrel实现
 * @author xiachao
 * @date 2019/2/12
 */
public class SquirrelLock extends AbstractCacheLock implements DistributedLock {

    private RedisStoreClient storeClient;

    private String category;

    private String LOCK_FLAG = "LOCKED";

    public static class Builder extends AbstractCacheLock.BaseBuilder{
        private String category;
        private String clusterName;
        private String routerType;
        private boolean spinLock = true;

        private int timeout = 1000;

        private int expire = 100;

        public Builder timeout(int timeout){
            this.timeout = timeout;
            return this;
        }
        public Builder expire(int expire){
            this.expire = expire;
            return this;
        }
        public Builder clusterName(String clusterName){
            this.clusterName = clusterName;
            return this;
        }
        public Builder category(String category){
            this.category = category;
            return this;
        }
        public Builder routerType(String routerType){
            this.routerType = routerType;
            return this;
        }
        public Builder spinLock(boolean spinLock){
            this.spinLock = spinLock;
            return this;
        }
        @Override
        public DistributedLock build(){
            RedisStoreClient baseClient = new RedisClientBuilder(clusterName)
                    .readTimeout(timeout)
                    .routerType(routerType)
                    .build();
            SquirrelLock lock = new SquirrelLock();
            lock.storeClient = baseClient;
            lock.category = category;
            lock.expire = expire;
            lock.spinLock = spinLock;
            return lock;
        }
    }

    @Override
    public boolean lock(String key, String owner, long timeout) {
        return this.trylock(key, owner, timeout);
    }

    @Override
    public boolean lock(String key, String owner) {
        return this.lock(key, DEFAULT_OWNER, timeout);
    }

    @Override
    public boolean lock(String key) {
        return this.lock(key, DEFAULT_OWNER);
    }

    @Override
    public boolean trylock(String key, String owner, long timeout) {
        StoreKey storeKey = new StoreKey(this.category, owner);
        while(timeout > 0){
            long currentTime = System.currentTimeMillis();
            boolean ret = this.storeClient.setnx(storeKey, LOCK_FLAG, this.expire);
            if(ret){
                return true;
            }
            if(spinLock == false){
                return false;
            }
            try{
                Thread.sleep(this.spinCycle);
            }catch (InterruptedException e){
                //do nothing
            }
            timeout -= System.currentTimeMillis() - currentTime;
        }
        return false;
    }

    @Override
    public boolean unlock(String key, String owner) {
        return this.storeClient.delete(new StoreKey(category, owner));
    }

    @Override
    public boolean unlock(String key) {
        return this.unlock(key, DEFAULT_OWNER);
    }
}
